﻿using System.Windows;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using Slma.OData;

// Author: Miroslav Matula m.matula@alutam.cz 
// Date : 9.9.2010

namespace System.Windows.Controls
{
  [TemplatePart(Name = ODataSource_elementErrorTitle, Type = typeof(TextBlock))]
  public class ODataSource : Control
  {
    private const string ODataSource_elementErrorTitle = "ErrorMessage";
    public ODataSource()
    {
      this.DefaultStyleKey = typeof(ODataSource);

      AutoLoad = true;
      LoadSize = 30;
      IncludeTotalCount = true;

      CanLoad = true;
      IsBusy = false;

      Loaded += new RoutedEventHandler(ODataSource_Loaded);
    }

    void ODataSource_Loaded(object sender, RoutedEventArgs e)
    {
      try {
        //if(DataServiceContext == null) return;
        if(DataServiceContext == null) throw new Exception("DataServiceContext cannot be null");
        if(string.IsNullOrWhiteSpace(EntitySetName)) throw new Exception("EntitySetName cannot be null");

        //vytvořím data collection, pro zobrazení dat
      var data = new ODataSourceView(DataServiceContext, EntitySetName);
      data.LoadSize = LoadSize;
      data.IncludeTotalCount = IncludeTotalCount;

      data.PropertyChanged += new ComponentModel.PropertyChangedEventHandler(data_PropertyChanged);
      data.DataLoaded += new AsyncCompletedEventHandler(data_DataLoaded);
      data.SubmittedChanges += new AsyncCompletedEventHandler(data_SubmitedChanges);
      data.AddingNewEntity += new EventHandler<EventEntityAddingNewEntityArgs>(data_AddedNew);
      data.CurrentChanged += (s, a) => {
        CurrentItem = Data.CurrentItem;
        CurrentPrimaryKey = Data.CurrentPrimaryKey;
        if(CurrentItemChanged != null) CurrentItemChanged(this,e);
      };
      this.Data = data;
      if(!System.ComponentModel.DesignerProperties.IsInDesignTool) {
        if(AutoLoad) { //AutoLoad
          // Spustí LoadData až proběhne load celé stránky
          this.Dispatcher.BeginInvoke(() => {
            using(this.Data.DeferRefresh()) {
              ApplySortDescriptions();
              ApplyFilterDescriptions();
            }
          });
        }
      } else {
        // Design Mode Data
        var ent = data.CreateEntity();
        data.Add(ent);
      }
      ValidateProperties();
      } catch(Exception ex) {
        this.SetErrorMessage(ex.Message);
        return;
      }
    }


    public bool AutoLoad { get; set; } //one-way ODataSource->ODataSourceView
    public int LoadSize { get; set; } //one-way ODataSource->ODataSourceView
    public bool IncludeTotalCount { get; set; } //one-way ODataSource->ODataSourceView
    private TextBlock _errorMessage;

    private void ValidateProperties()
    {
      string error = null;
      if(DataServiceContext == null) error += "DataServiceContext is Null";
      SetErrorMessage(error);
    }



    #region -- Dependency Property -----------------------------
    /// <summary>
    /// Data context. Obsahuje URI adresu služby a zajišťuje operace CRUD
    /// </summary>
    public System.Data.Services.Client.DataServiceContext DataServiceContext
    {
      get { return (System.Data.Services.Client.DataServiceContext)GetValue(DataServiceContextProperty); }
      set { SetValue(DataServiceContextProperty, value); }
    }
    public static readonly DependencyProperty DataServiceContextProperty =
        DependencyProperty.Register("DataServiceContext", typeof(System.Data.Services.Client.DataServiceContext), typeof(ODataSource)
        , new PropertyMetadata((s, a) => {
          var se = s as ODataSource;
          se.ValidateProperties();
        }));

    /// <summary>
    /// Název entity, se kterou se pracuje
    /// </summary>
    public string EntitySetName
    {
      get { return (string)GetValue(EntitySetNameProperty); }
      set { SetValue(EntitySetNameProperty, value); }
    }
    public static readonly DependencyProperty EntitySetNameProperty =
        DependencyProperty.Register("EntitySetName", typeof(string), typeof(ODataSource), new PropertyMetadata((s, a) => {
          var se = s as ODataSource;
          se.ValidateProperties();
        }));
    



    public ODataSourceView Data { get; private set; }
    //--
    public bool IsBusy
    {
      get { return (bool)GetValue(IsBusyProperty); }
      private set { SetValue(IsBusyProperty, value); }
    }
    public static readonly DependencyProperty IsBusyProperty =
        DependencyProperty.Register("IsBusy", typeof(bool), typeof(ODataSource), null);
    //--
    public bool CanLoad
    {
      get { return (bool)GetValue(CanLoadProperty); }
      private set { SetValue(CanLoadProperty, value); }
    }
    public static readonly DependencyProperty CanLoadProperty =
        DependencyProperty.Register("CanLoad", typeof(bool), typeof(ODataSource), null);
    //--
    public bool HasChanges
    {
      get { return (bool)GetValue(HasChangesProperty); }
      private set { SetValue(HasChangesProperty, value); }
    }
    public static readonly DependencyProperty HasChangesProperty =
        DependencyProperty.Register("HasChanges", typeof(bool), typeof(ODataSource), null);
    //--
    public string QueryURL
    {
      get { return (string)GetValue(QueryURLProperty); }
      private set { SetValue(QueryURLProperty, value); }
    }
    public static readonly DependencyProperty QueryURLProperty =
        DependencyProperty.Register("QueryURL", typeof(string), typeof(ODataSource), null);
    //--
    public bool IsLoading
    {
      get { return (bool)GetValue(IsLoadingProperty); }
      set { SetValue(IsLoadingProperty, value); }
    }
    public static readonly DependencyProperty IsLoadingProperty =
        DependencyProperty.Register("IsLoading", typeof(bool), typeof(ODataSource), null);
    //--
    public bool IsSubmitting
    {
      get { return (bool)GetValue(IsSubmittingProperty); }
      set { SetValue(IsSubmittingProperty, value); }
    }
    public static readonly DependencyProperty IsSubmittingProperty =
        DependencyProperty.Register("IsSubmitting", typeof(bool), typeof(ODataSource), null);
    //--
    public object CurrentItem
    {
      get { return (object)GetValue(CurrentItemProperty); }
      private set { SetValue(CurrentItemProperty, value); }
    }
    public static readonly DependencyProperty CurrentItemProperty =
        DependencyProperty.Register("CurrentItem", typeof(object), typeof(ODataSource), null);
    //--
    public string CurrentPrimaryKey
    {
      get { return (string)GetValue(CurrentPrimaryKeyProperty); }
      private set { SetValue(CurrentPrimaryKeyProperty, value); }
    }
    public static readonly DependencyProperty CurrentPrimaryKeyProperty =
        DependencyProperty.Register("CurrentPrimaryKey", typeof(string), typeof(ODataSource), null);
    //--
    //todo:pozor je to jednosmerné ODataSource->ODataSourceView
    public string ForeignKey
    {
      get { return (string)GetValue(ForeignKeyProperty); }
      set { SetValue(ForeignKeyProperty, value); }
    }
    public static readonly DependencyProperty ForeignKeyProperty =
        DependencyProperty.Register("ForeignKey", typeof(string), typeof(ODataSource), new PropertyMetadata(null, ForeignKeyProperty_changed));
    private static void ForeignKeyProperty_changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
      ((ODataSource)d).Data.ForeignKey = (string)e.NewValue;
    }
    //--

    #endregion


    //-- Filter Descriptor ---------------------
    public FilterDescriptorOData FilterDescriptions
    {
      get { return _filterDescriptions; }
      set
      {
        if(_filterDescriptions == value) return;
        _filterDescriptions = value;
        ApplyFilterDescriptions();
      }
    }
    private FilterDescriptorOData _filterDescriptions;

    public FilterDescriptorOData FilterDescriptionsSecondPart
    {
      get { return _filterDescriptionsSecondPart; }
      set
      {
        if(_filterDescriptionsSecondPart == value) return;
        _filterDescriptionsSecondPart = value;
        ApplyFilterDescriptions();
      }
    }
    private FilterDescriptorOData _filterDescriptionsSecondPart;

    private SortDescriptionListOData _sortDescriptions;
    public SortDescriptionListOData SortDescriptions //SortDescriptionListOData
    {
      get
      {
        if(_sortDescriptions == null) _sortDescriptions = new SortDescriptionListOData();
        return _sortDescriptions;
      }
    }


    private FilterDescriptorOData fd2_root = null;

    public event AsyncCompletedEventHandler DataLoaded;
    public event AsyncCompletedEventHandler SubmittedChanges;
    public event EventHandler<EventEntityAddingNewEntityArgs> AddingNewEntity;
    public event EventHandler CurrentItemChanged;

    public override void OnApplyTemplate()
    {
      _errorMessage = GetTemplateChild(ODataSource_elementErrorTitle) as TextBlock;
      base.OnApplyTemplate();
    }
    public void SetErrorMessage(string errorMessage)
    {
      if(string.IsNullOrWhiteSpace(errorMessage)) {
        if(Visibility == System.Windows.Visibility.Visible) {
          if(_errorMessage != null) _errorMessage.Inlines.Clear();
          Visibility = System.Windows.Visibility.Collapsed;
        }
      } else {
        Visibility = System.Windows.Visibility.Visible;
        this.Dispatcher.BeginInvoke(() => {
          if(_errorMessage != null) {
            _errorMessage.Padding = new Thickness(5);
            _errorMessage.Text = errorMessage;
          }
        });
      }
    }

    public void ClearFilterDescription2()
    {
      fd2_root=null;
    }
    public void AndFilterDescription2(string propertyName_, int operátor_, string value_)
    {
      var fd = new FilterDescriptorExpressionOData() { PropertyName = propertyName_, Operator = (FilterDescriptorExpressionOperator)operátor_, Value = value_ };
      if(fd2_root==null) {
        fd2_root = fd;
      } else {
        fd2_root = new FilterDescriptorLogicalOData() {
          Operator = FilterDescriptorLogicalOperator.And,
          Left = fd2_root,
          Right = fd
        };
      }
    }
    public void ApplyFilterDescription2()
    {
      FilterDescriptionsSecondPart = fd2_root;
    }
 
    public void LoadData()
    {
    }

    void data_AddedNew(object sender, EventEntityAddingNewEntityArgs e)
    {
      if(AddingNewEntity != null) AddingNewEntity(this,e);
    }

    void data_SubmitedChanges(object sender, AsyncCompletedEventArgs e)
    {
      if(SubmittedChanges != null) {
        Dispatcher.BeginInvoke(() => {
          SubmittedChanges(this, e);
        });
      }
    }

    void data_DataLoaded(object sender, AsyncCompletedEventArgs e)
    {
      if(DataLoaded != null) {
        Dispatcher.BeginInvoke(() => {
          DataLoaded(this, e);
        });
      }
    }

    void data_PropertyChanged(object sender, ComponentModel.PropertyChangedEventArgs e)
    {
      switch(e.PropertyName) {
        case "IsLoading": this.IsLoading = Data.IsLoading; break;
        case "IsSubmitting": this.IsSubmitting = Data.IsSubmitting; break;
        case "IsBusy": this.IsBusy = Data.IsBusy; break;
        case "CanLoad": this.CanLoad = Data.CanLoad; break;
        case "HasChanges": this.HasChanges = Data.HasChanges; break;
        case "QueryURL": this.QueryURL = Data.QueryURL; break;
      }
    }

    public void Clear()
    {
      Data.Clear();
    }
    protected void ApplyFilterDescriptions()
    {
      if(Data == null) return;
      if(_filterDescriptions != null && _filterDescriptionsSecondPart != null) {
        Data.Filter = FilterDescriptorOData.ConvertToExpression(Data.ODataEntityType, new FilterDescriptorLogicalOData() {
          Operator = FilterDescriptorLogicalOperator.And,
          Left = _filterDescriptions,
          Right = _filterDescriptionsSecondPart
        });
      } else if(_filterDescriptions != null) {
        Data.Filter = FilterDescriptorOData.ConvertToExpression(Data.ODataEntityType, _filterDescriptions);
      } else if(_filterDescriptionsSecondPart != null) {
        Data.Filter = FilterDescriptorOData.ConvertToExpression(Data.ODataEntityType, _filterDescriptionsSecondPart);
      } else Data.Filter = null;
    }
    public void ApplySortDescriptions()
    {
      ODataSourceView dv = Data;
      using(dv.DeferRefresh()) {
        dv.SortDescriptions.Clear();
          foreach(var item in this._sortDescriptions) {
            dv.SortDescriptions.Add(new SortDescription() {
              PropertyName = item.PropertyName,
              Direction = ((item.Direction == SortDescriptionDirectionOData.Ascending) ? ListSortDirection.Ascending : ListSortDirection.Descending)
            });
          }
      }
    }

    public void RejectChanges()
    {
      Data.RejectChanges();
    }

    public void RefreshData()
    {
      Data.Refresh();
    }

    public IAsyncResult SubmitChanges(Action<AsyncCompletedEventArgs> CallBack_)
    {
      IAsyncResult res = Data.SubmitChanges((arg) => {
        if(CallBack_ != null) CallBack_(arg);
      });
      return res;
    }
    public void CancelLoading()
    {
      Data.CancelLoading();
    }
    public void CancelSubmitting()
    {
      Data.CancelSubmitting();
    }

  }
}
